﻿using CommonDevelop;
using CommonDevelop.Cache;
using CommonDevelop.DB;
using CommonDevelop.Operator;
using CommonEngine;
using CommonEngine.Security;
using CommonFP;
using ManageDB;
using ManageDto;
using ManageRepository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ManageService
{
    /// <summary>
    /// 用户服务层接口
    /// </summary>
    public interface IUserService : IBaseService<Base_User>
    {
        Validation<UserDto> GetUserDto(string UserId);
        Validation<UserDto> GetUserByAccount(string account);
        Validation<UserDto> CheckLogin(string account, string password);

        IEnumerable<UserDto> GetDtosByCompanyId(string CompanyId);
        IEnumerable<UserDto> GetDtosByDeptId(string DeptId);
        IEnumerable<UserDto> GetDtosSets(Pagination Pag, string CompanyId, string DeptId, string keyword);

        /// <summary>
        /// 账户不能重复
        /// </summary>
        bool ExistAccount(string account, string UserId);
        /// <summary>
        /// 修改用户登录密码
        /// </summary>
        bool RevisePassword(string UserId, string newPassword, string oldPassword);
        /// <summary>
        /// 重置密码(000000)
        /// </summary>
        bool ResetPassword(string UserId);

        /// <summary>
        /// 返回数据库中还没有的账号清单（辅助导入导出）
        /// </summary>
        Validation<List<string>> FilterAccount(List<string> AccountLs);

        string Init_Admin();

        UserDto FullUserDto(Base_User User);
    }

    /// <summary>
    /// 用户服务层实现
    /// </summary>
    public class UserService : BaseService<Base_User, IUserRepository>, IUserService
    {
        private readonly ReasonableCache<UserModel> cachePools;
        /// <summary>
        /// 构造注入基类依赖
        /// </summary>
        public UserService(IUserRepository repository, IDCSCache MemoryCache)
            : base(repository)
        {
            this.cachePools = new ReasonableCache<UserModel>(MemoryCache, this.Get_DB_HOF);
        }

        #region--override--

        public override string BuildSeq()
            => SugarBizDB.BuildSeq_01("User");

        public override Option<TVO> Get<TVO>(object keyValue)
        {
            if (typeof(TVO).Equals(typeof(UserDto)))
            {
                UserDto result = keyValue.IsValidData()
                    .Bind(a => this.GetUserDto(a.ToString()))
                    .Outcome();

                return result as TVO;
            }
            else
                return base.Get<TVO>(keyValue);
        }

        public override Validation<bool> Add<TVO>(TVO En)
        {
            if (typeof(TVO).Equals(typeof(UserDto)))
            {
                UserDto result = En as UserDto;
                var pipe1 = FP.MakePipe<UserDto, Base_User>(a => FP.Async(a.MapTo<Base_User>()));

                var v = from Dto in FP.Async(result.IsValidData())
                        from uB in pipe1(Dto)
                        let b = this.repository_DB.Add_Entity(uB, true)
                        let r = this.ResetPassword(Dto.UserId)
                        select b && r;

                return v.Recover(ex => FP.Error(ex.Message)).Result;
            }
            else
                return base.Add<TVO>(En);
        }

        public override Validation<bool> Save<TVO>(TVO En)
        {
            if (typeof(TVO).Equals(typeof(UserDto)))
            {
                UserDto result = En as UserDto;
                var pipe1 = FP.MakePipe<UserDto, Base_User>(a => FP.Async(a.MapTo<Base_User>()));
                var pipe3 = FP.MakePipe<UserDto, Base_User>(a => this.GetEntity(a.UserId));

                var v = from Dto in FP.Async(result.IsValidData())
                        from uB in pipe1(Dto)
                        from olduB in pipe3(Dto)
                        let NewuB = olduB.MergeEn(uB)
                        let b = this.repository_DB.Update_Entity(NewuB, true)
                        select b;

                var t = v.Recover(ex => FP.Error(ex.Message));
                t.Wait();

                return t.Result;
            }
            else
                return base.Save<TVO>(En);
        }

        #endregion

        #region--CacheRepository--

        private async Task<Validation<UserModel>> Get_DB_HOF(string UserId)
        {
            await Task.CompletedTask;
            try
            {
                Base_User bu = this.repository_DB.GetEntity(UserId);
                if (bu == null)
                    return FP.Invalid("Entity is null");

                UserModel model = bu.MapTo<UserModel>();
                var com = SugarBizDB.GetReadOnlyOne<Base_Company>(bu.CompanyId);
                var dept = SugarBizDB.GetReadOnlyOne<Base_Dept>(bu.DepartmentId);

                model.CompanyName = com == null ? "" : com.FullName;
                model.DepartmentName = dept == null ? "" : dept.FullName;
                return model;
            }
            catch (Exception e)
            {
                return FP.Invalid(e.Message);
            }
        }

        public UserDto FullUserDto(Base_User User)
            => (from en in User.ToOption()
                let dto = en.MapTo<UserDto>()
                let uw = this.cachePools.GetModel(en.UserId)
                let uwdto = uw.MapTo<UserDto>()
                select dto.MergeDto(uwdto))
            .Outcome();

        public bool Replace(string id, UserModel t)
            => this.cachePools.ReplaceModel(id, t);

        #endregion

        #region--登录操作--

        public Validation<UserDto> GetUserDto(string UserId)
        {
            var pipe01 = FP.MakePipe<string, Base_User>(n => this.GetEntity(n));
            var pipe02 = FP.MakePipe<string, UserModel>(n => this.cachePools.GetModel(n));

            var outcome =
                from ac in FP.Async(UserId.IsValidData())
                from bb in pipe01(ac)
                let dto = bb.MapTo<UserDto>()
                from ww in pipe02(bb.UserId)
                let dtoww = ww.MapTo<UserDto>()
                select dto.MergeDto(dtoww);

            var t = outcome.Recover(ex => FP.Error(ex.Message));
            t.Wait();
            return t.Result;
        }

        public Validation<UserDto> GetUserByAccount(string account)
        {
            var v = from b in FP.Try(() => this.repository_DB.GetUserByAccount(account))
                    let dto = b.MapTo<UserDto>()
                    from w in FP.Try(() => this.cachePools.GetModel(b.UserId))
                    let dtow = w.MapTo<UserDto>()
                    select dto.MergeDto(dtow);

            return v.Run().ToValidation();
        }
        /// <summary>
        /// 验证登录
        /// </summary>
        public Validation<UserDto> CheckLogin(string account, string password)
        {
            var userBase = this.repository_DB.GetUserByAccount(account);
            if (userBase == null)
                return FP.Invalid("账户不存在!");

            if (userBase.IsDelete)
                return FP.Invalid("账户被系统锁定,请联系管理员!");

            // MD5 32位 小写
            string dbPassword = Md5Helper.Encrypt(DESEncrypt.Encrypt(password.ToLower(), userBase.Secretkey).ToLower(), 32).ToLower();
            if (dbPassword != userBase.Password)
                return FP.Invalid("密码和账户名不匹配!");

            string UserId = userBase.UserId;
            var v = from w in FP.Try(() => this.cachePools.GetModel(UserId))
                    let dto = userBase.MapTo<UserDto>()
                    let dtow = w.MapTo<UserDto>()
                    select dto.MergeDto(dtow);

            return v.Run().ToValidation();
        }

        #endregion

        #region--辅助交互操作-----

        public IEnumerable<UserDto> GetDtosByCompanyId(string CompanyId)
        {
            var ls = this.repository_DB.GetJobByCompanyId(CompanyId);
            List<UserDto> result = new List<UserDto>();
            foreach (var v in ls)
                result.Add(this.FullUserDto(v));

            return result;
        }

        public IEnumerable<UserDto> GetDtosByDeptId(string DeptId)
        {
            var ls = this.repository_DB.GetJobByDeptId(DeptId);
            List<UserDto> result = new List<UserDto>();
            foreach (var v in ls)
                result.Add(this.FullUserDto(v));

            return result;
        }

        public IEnumerable<UserDto> GetDtosSets(Pagination Pag, string CompanyId, string DeptId, string keyword)
        {
            var ls = this.repository_DB.GetUserJobSets(Pag, CompanyId, DeptId, keyword);
            List<UserDto> result = new List<UserDto>();
            foreach (var v in ls)
                result.Add(this.FullUserDto(v));

            return result;
        }

        #endregion

        #region--账号操作--

        /// <summary>
        /// 验证账号
        /// </summary>
        public bool ExistAccount(string account, string UserId)
        {
            var expression = LinqExtend.True<Base_User>();
            expression = expression.And(t => t.Account == account);

            if (!string.IsNullOrEmpty(UserId))
                expression = expression.And(t => t.UserId != UserId);

            return FP.Try(() => this.repository_DB.GetEntity(expression, false))
                .Run().Match(e => false, a => a == null ? false : true);
        }
        /// <summary>
        /// 修改用户登录密码
        /// </summary>
        public bool RevisePassword(string UserId, string newPassword, string oldPassword)
        {
            var UI = OperatorProvider.Provider.Current();
            if (UI == null)
                return false;

            if (UI.UserId.Equal(UserId))
                return false;

            var v = from user in this.GetEntity(UserId)
                    from p in FP.Async(Md5Helper.Encrypt(DESEncrypt.Encrypt(oldPassword, user.Secretkey).ToLower(), 32).ToLower())
                    select p == user.Password ? user : null;

            var t = v.Map(userbase => this.repository_DB.RevisePassword(newPassword, userbase.UserId, UI.UserId, UI.FullName))
                .OrElse(() => FP.Async(false));

            t.Wait();
            return t.Result;
        }
        /// <summary>
        /// 重置密码(000000)
        /// </summary>
        public bool ResetPassword(string UserId)
        {
            var UI = OperatorProvider.Provider.Current();
            if (UI == null)
                return false;

            if (UI.UserId.Equal(UserId))
                return false;

            var v = from user in this.GetEntity(UserId)
                    from p in FP.Async(this.repository_DB.RevisePassword("000000", user.UserId, UI.UserId, UI.FullName))
                    select p;
            var t = v.OrElse(() => FP.Async(false));

            t.Wait();
            return t.Result;
        }

        //辅助导入导出
        public Validation<List<string>> FilterAccount(List<string> AccountLs)
        {
            var v = from ls in FP.Async(AccountLs.AsEnumerable().IsValidData().Outcome())
                    from vls in FP.Async(this.repository_DB.GetList(a => a.IsDelete == false && ls.Contains(a.Account)))
                    let als = vls.Select(a => a.Account)
                    select FP.Valid(ls.Except(als).ToList()); //取差集--返回未进表的账号

            var t = v.Recover(ex => FP.Error(ex.Message));
            t.Wait();
            return t.Result;
        }

        /// <summary>
        /// 初始化管理员
        /// </summary>
        public string Init_Admin()
        {
            UserDto admin = this.GetUserByAccount("admin").Outcome();
            if (admin == null)
            {
                admin = new UserDto() { UserId = Guid.NewGuid().ToString() };
                string Secretkey = CommonOP.MakeSecretkey();
                string Password = Md5Helper.Encrypt(DESEncrypt.Encrypt("123123", Secretkey).ToLower(), 32).ToLower();

                admin.ApplyKey = "2020-2021";
                admin.Account = "admin";
                admin.Password = Password;
                admin.Secretkey = Secretkey;
                admin.Gender = (int)Gender.Male;
                admin.IsAdmin = true;
                admin.Token = string.Empty;

                admin.FullName = "admin";
                admin.JobNumber = this.BuildSeq();
                admin.NickName = "admin";
                admin.HeadIcon = "/Playground/Content/patterns/profile_small.jpg";
                admin.Mobile = "15011785735";

                admin.BusinessID = "2020SaasBiz567";

                admin.CompanyId = string.Empty;
                admin.DepartmentId = string.Empty;

                admin.ModifyUserId = admin.UserId;
                admin.ModifyUserName = "admin";

                var t = this.Add(admin).Outcome(e => e ? "had" : "None");
                if (t.Errors.IsEmpty())
                    return t.result;
                else
                    return t.Errors;
            }
            else
                return "had";
        }

        #endregion
    }
}
